/**
 * Live GPS Tracking Application
 * Thread which uploads position using HTTP
 *
 * Copyright 2009, Ivar Clemens
 */

package nl.ivarclemens.gpstrack;

import java.io.IOException;
import java.io.DataOutputStream;
import javax.microedition.io.HttpConnection;
import javax.microedition.io.Connector;
import java.io.ByteArrayOutputStream;

import nl.ivarclemens.gpstrack.track.LiveTrack;

public class HTTPThread extends LiveTrack {
    private WorkerThread thread = null;
    private boolean allowTransmission = false;

    private String httpAddress = null;

    public HTTPThread()
    {
        thread = new WorkerThread(this);
        thread.start();
    }

    public void setDestination(String httpAddress)
    {
        this.httpAddress = httpAddress;
    }

    public void startTransmission()
    {
        this.allowTransmission = true;
    }

    public void stopTransmission()
    {
        this.allowTransmission = false;
    }

    public boolean mayTransmit()
    {
        return this.allowTransmission;
    }

    /**
     * POSTs a message to a remote server
     */
    private boolean postData(byte[] requestBody)
    {
        HttpConnection conn = null;

        if(httpAddress == null || !this.allowTransmission)
            return false;

        try {
            conn = (HttpConnection) Connector.open(httpAddress, Connector.WRITE);

            conn.setRequestMethod(HttpConnection.POST);
            conn.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
            conn.setRequestProperty("Content-Length", requestBody.length + "");

            DataOutputStream output = conn.openDataOutputStream();

            for (int i = 0; i < requestBody.length; i++) {
                output.writeByte(requestBody[i]);
            }

            output.flush();
            output.close();

            conn.close();
        } catch(java.io.IOException exception) {
            return false;
        }
        
        return true;
    }

    /**
     * Encodes the track in a binary fashion.
     */
    public byte[] encodeTrackAsBLOB(int pnt) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        dos.writeByte(0x3C);    // Write <
        dos.writeByte(0x01);    // Encoding 1

        dos.writeLong(pnt);     // Number of points

        for(int i = 0; i < pnt; i++) {
            // Only send first timestamp completely
            if(i == 0) {
                dos.writeLong(this.getTime(i));
            } else {
                long deltaT = this.getTime(i) - this.getTime(i - 1);
                dos.writeShort((short) deltaT);
            }

            dos.writeDouble(this.getLon(i));
            dos.writeDouble(this.getLat(i));
        }

        dos.writeByte(0x3E);    // Write >
        dos.flush();

        return bos.toByteArray();
    }

    public byte[] encodeTrackAsString(int pnt) throws IOException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(bos);

        dos.writeByte(0x3C);
        dos.writeByte(0x65);    // Encoding A

        for(int i = 0; i < pnt; i++) {           
           dos.writeChars(Long.toString(this.getTime(i)));
           dos.writeChar(',');
           dos.writeChars(Double.toString(this.getLon(i)));
           dos.writeChar(',');
           dos.writeChars(Double.toString(this.getLat(i)));
           dos.writeChar(';');
        }

        dos.writeByte(0x3C);
        dos.flush();

        return bos.toByteArray();
    }

    public boolean sendTrack()
    {
        int untilPtr = this.numPoints();
        boolean result = false;

        if(untilPtr == 0 || httpAddress == null || !this.mayTransmit())
            return false;

        // Maximum payload is 100 track points
        if(untilPtr > 100)
            untilPtr = 100;

        try {
            final byte[] data = encodeTrackAsBLOB(untilPtr);
            
            result = postData(data);

            // TODO: Check transmission using some kind of checksum

            if(result == true) {
                // Fixme: Option to compact and send track from non-zero coords
                this.remapTrack(untilPtr, 0);
            }

        } catch(Exception e) {
            return false;
        }

        return result;
    }

    public class WorkerThread extends Thread
    {
        HTTPThread parent;

        public WorkerThread(HTTPThread parent)
        {
            this.parent = parent;
        }

        public void run()
        {
            while(true) {
                if(parent.mayTransmit()) {

                    parent.sendTrack();

                    try {
                        Thread.sleep(20000);
                    } catch(Exception e) {}
                } else {
                    try {
                        Thread.sleep(30000);
                    } catch(Exception e) {}
                }
            }
        }
    }
}
